///////////////////////////////////////////////////////////////////////////////
// Name:        xmlloadmodel_opencv.cpp
// Purpose:     Load data from xml resources
// Copyright:   (c) 2006, Ivan Laptev, TT Solutions
// License:     GPL, Please contact us for commercial license.
//              http://www.tt-solutions.com/
///////////////////////////////////////////////////////////////////////////////
#include "objectdet/xmlloadmodel.h"

#include <iostream>
#include <sstream>

// directly access OpenCV functions
#include <cxcore.h>

// #define DEBUG_XMLLOAD

void XmlSpitReadingInfo(CvFileStorage * fs, CvFileNode * node, char const * type, char const * name)
{
#ifdef DEBUG_XMLLOAD
    std::cout << "Reading " << type << " by name(" << name << ") from " << (void*)node << std::flush;
    if (node)
        std::cout << "(" << cvGetFileNodeName(node) << ")" << std::flush;
    std::cout << "\n" << std::flush;
#endif
}

double myReadRealByName(CvFileStorage * fs, CvFileNode * node, char const * name)
{
    XmlSpitReadingInfo(fs, node, "real", name);
#ifdef DEBUG_XMLLOAD
    std::cout << "real(" << cvReadRealByName(fs, node, name) << ")\n" << std::flush;
#endif
    return cvReadRealByName(fs, node, name);
}

int myReadIntByName(CvFileStorage * fs, CvFileNode * node, char const * name)
{
    XmlSpitReadingInfo(fs, node, "int", name);
#ifdef DEBUG_XMLLOAD
    std::cout << "int(" << cvReadIntByName(fs, node, name) << ")\n" << std::flush;
#endif
    return cvReadIntByName(fs, node, name);
}

char const * myReadStringByName(CvFileStorage * fs, CvFileNode * node, char const * name)
{
    XmlSpitReadingInfo(fs, node, "string", name);
#ifdef DEBUG_XMLLOAD
    std::cout << "string(" << cvReadStringByName(fs, node, name) << ")\n" << std::flush;
#endif
    return cvReadStringByName(fs, node, name);
}

CvFileNode * myGetFileNodeByName(CvFileStorage * fs, CvFileNode * node, char const * name)
{
    XmlSpitReadingInfo(fs, node, "node", name);
    return cvGetFileNodeByName(fs, node, name);
}

void SpitNodeInfo( CvFileNode * node )
{
#ifdef DEBUG_XMLLOAD
    std::cout << "node(" << (void*)node << ") " << std::flush;
    if (node)
        std::cout << cvGetFileNodeName(node);
    std::cout << "\n" << std::flush;
#endif
}

IMPEXP
void xmlloadmodel(std::string const & file, Model & model)
{
    pFileStorage fs = OpenFileStorage(file, RWM_Read);
	if (!fs)
    {
        std::ostringstream oss;
        oss << "Could not read classifier " << file.c_str();
		throw std::runtime_error(oss.str());
	}
    CvFileNode * rootModelNode = myGetFileNodeByName(fs.get(), NULL, "model");
    SpitNodeInfo( rootModelNode );
	// assert( std::string("model") == cvGetFileNodeName(rootModelNode) );
    assert( std::string("adacascadeclass") == myReadStringByName(fs.get(), rootModelNode, "fun") );
    SpitNodeInfo( rootModelNode );
    int stage_count = myReadIntByName(fs.get(), rootModelNode, "stagemodel_count");
	int histblock_winsize_x, histblock_size_x;
	int histblock_winsize_y, histblock_size_y;
	if (cvGetFileNodeByName(fs.get(), rootModelNode, "histblock_winsize_x") && 
		cvGetFileNodeByName(fs.get(), rootModelNode, "histblock_winsize_y") &&
		cvGetFileNodeByName(fs.get(), rootModelNode, "histblock_size_x") &&
		cvGetFileNodeByName(fs.get(), rootModelNode, "histblock_size_y"))
    {
		histblock_winsize_x = myReadIntByName(fs.get(), rootModelNode, "histblock_winsize_x");
		histblock_winsize_y = myReadIntByName(fs.get(), rootModelNode, "histblock_winsize_y");
		histblock_size_x = myReadIntByName(fs.get(), rootModelNode, "histblock_size_x");
		histblock_size_y = myReadIntByName(fs.get(), rootModelNode, "histblock_size_y");
	}
    else
    {
		histblock_winsize_x = 20; // default x-size value
		histblock_winsize_y = 10; // default y-size value
		histblock_size_x = 5; // default x-block size
		histblock_size_y = 5; // default y-block size
	}

    // default histogram type
	std::string histtype("orienthistmod");
	if (cvGetFileNodeByName(fs.get(), rootModelNode, "histtype"))
    {
		histtype = myReadStringByName(fs.get(), rootModelNode, "histtype");
    }

	//printf("parameters: %d,%d %d,%d hittype: '%s'\n",histblock_winsize_x,histblock_winsize_y,
	//	histblock_size_x,histblock_size_y,histtype);

	assert( stage_count >= 0 );
    for (int stagei = 0; stagei < stage_count; ++stagei)
    {
        std::stringstream buf;
        buf << "stagemodel" << (stagei + 1);
        CvFileNode * stageNode = myGetFileNodeByName( fs.get(), rootModelNode, buf.str().c_str() );
        SpitNodeInfo( stageNode );
        assert( stageNode );
        Stage stage;
        stage.stage_threshold = myReadRealByName(fs.get(), stageNode, "stage_threshold");
        stage.exitflag = myReadIntByName(fs.get(), stageNode, "exitflag");
		stage.histblock_winsize_x=histblock_winsize_x;
		stage.histblock_winsize_y=histblock_winsize_y;
		stage.histblock_size_x=histblock_size_x;
		stage.histblock_size_y=histblock_size_y;
		stage.histtype = histtype;

        int rule_count = myReadIntByName(fs.get(), stageNode, "member_count");
        assert( rule_count >= 0 );
        for (int rulei = 0; rulei < rule_count; ++rulei)
        {
            std::stringstream rulebuf;
            rulebuf << "member" << (rulei + 1);
            CvFileNode * memberNode = cvGetFileNodeByName( fs.get(), stageNode, rulebuf.str().c_str() );
            assert( memberNode );
            StageRule rule;
            rule.Alpha = myReadRealByName(fs.get(), memberNode, "Alpha");
            rule.Z = myReadRealByName(fs.get(), memberNode, "Z");
            rule.WeightedErr = myReadRealByName(fs.get(), memberNode, "WeightedErr");
            rule.ErrBound = myReadRealByName(fs.get(), memberNode, "ErrBound");

            CvFileNode * ruleNode = cvGetFileNodeByName( fs.get(), memberNode, "rule" );
            assert( ruleNode );
            rule.type = (StageRuleType)myReadIntByName(fs.get(), ruleNode, "ftype");
            rule.trainerror = myReadRealByName(fs.get(), ruleNode, "trainerror");

            CvFileNode * rectNode = cvGetFileNodeByName( fs.get(), ruleNode, "rect" );
            assert( rectNode );
            // Note: it seems we dont need that // '-1' is due to the fact that matlab has 1-based indexes instead of 0-based
            rule.left   = myReadIntByName(fs.get(), rectNode, "x"); // - 1;
            rule.top    = myReadIntByName(fs.get(), rectNode, "y"); // - 1;
            rule.width  = myReadIntByName(fs.get(), rectNode, "w");
            rule.height = myReadIntByName(fs.get(), rectNode, "h");

            CvFileNode * ruleModelNode = cvGetFileNodeByName( fs.get(), ruleNode, "rule_model" );
            assert( ruleModelNode );
            rule.separab = myReadRealByName(fs.get(), ruleModelNode, "separab");
            rule.b = myReadRealByName(fs.get(), ruleModelNode, "b");
            
            CvFileNode * wNode = cvGetFileNodeByName( fs.get(), ruleModelNode, "W" );
            assert( wNode );
            unsigned int wCount = myReadIntByName(fs.get(), wNode, "w_count");
            for (unsigned int wi = 0; wi < wCount; ++wi)
            {
                std::stringstream wbuf;
                wbuf << "w" << (wi + 1);
                rule.W.push_back( myReadRealByName(fs.get(), wNode, wbuf.str().c_str()) );
            }
            std::size_t size = 0;
            if (rule.type == SrtOneSquare)
            {
                size = 4; //12;
            }
            else if (rule.type == SrtTwoCols || rule.type == SrtTwoRows)
            {
                size = 8; //24;
            }
            else if (rule.type == SrtFourQuarters)
            {
                size = 16; //48;
            }
            else
            {
                assert( rule.type == (int)"unknown" );
            }
            assert( size == rule.W.size() );
            stage.rules.push_back( rule );
        }
        model.push_back( stage );
    }
}

